package com.example.zluserservice.framework.security;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.example.zluserservice.common.constant.CommonConstant;
import com.example.zluserservice.common.constant.UserCacheConstant;
import com.example.zluserservice.common.util.JwtUtil;
import com.example.zluserservice.framework.properties.JwtTokenManagerProperties;
import com.example.zluserservice.dto.response.UserResDTO;
import io.jsonwebtoken.Claims;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @Author zhenliu（孙凌岳）
 * @Description 授权-Jwt授权管理器
 * @Date 2025/6/15 23:19
 * @Version 1.0
 */
@Component
public class JwtTokenAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext requestAuthorizationContext) {
        //获取Request
        HttpServletRequest request = requestAuthorizationContext.getRequest();
        //获取uuidToken
        String userTokenFromRequest = request.getHeader(CommonConstant.USER_TOKEN);
        if(ObjectUtil.isEmpty(userTokenFromRequest)){
            return new AuthorizationDecision(false);
        }

        //从redis中获取jwtToken
        String jwtTokenKey = UserCacheConstant.JWT_TOKEN + userTokenFromRequest;
        String jwtToken = stringRedisTemplate.opsForValue().get(jwtTokenKey);
        if(ObjectUtil.isEmpty(jwtToken)){
            return new AuthorizationDecision(false);
        }

        //解析数据
        Claims claims = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtToken);

        //获取用户数据
        UserResDTO userResDTO = JSONUtil.toBean(String.valueOf(claims.get(CommonConstant.CURRENT_USER)), UserResDTO.class);

        //从redis中获取uuid
        String userTokenKey = UserCacheConstant.USER_TOKEN + userResDTO.getUsername();
        String userTokenFromRedis = stringRedisTemplate.opsForValue().get(userTokenKey);

        //判断前端Request的header里传来的UserToken和redis中的UserToken是否相等
        if(!userTokenFromRequest.equals(userTokenFromRedis)){
            return new AuthorizationDecision(false);
        }

        //判断Token的过期时间是够小于10分钟，如果小于十分钟，重置过期时间
        Long expire = stringRedisTemplate.opsForValue().getOperations().getExpire(jwtTokenKey);
        if(expire < 600){
            //续期策略：
            //① jwtToken需要重新生成
            //② userToken只需重新设置过期时间
            Map<String,Object> expireClaims = new HashMap<>();
            expireClaims.put(CommonConstant.CURRENT_USER,String.valueOf(claims.get(CommonConstant.CURRENT_USER)));

            String newJwtToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey()
                    , jwtTokenManagerProperties.getTtl(), claims);

            //过期时间计算
            Long ttl = Long.valueOf(jwtTokenManagerProperties.getTtl() / 1000);

            stringRedisTemplate.opsForValue().set(jwtTokenKey,newJwtToken,ttl, TimeUnit.SECONDS);

            stringRedisTemplate.expire(userTokenKey,ttl,TimeUnit.SECONDS);
        }

        //当前请求路径（例如：GET/user/**）
        String method = request.getMethod();
        String requestURI = request.getRequestURI();
        String targetUrl = method + requestURI;

        //验证用户是否拥有该权限
        for(String resourceRequestPath : userResDTO.getResourceRequestPaths()){
            boolean match = antPathMatcher.match(resourceRequestPath,targetUrl);
            if(match){
                return new AuthorizationDecision(true);
            }
        }

        //为了测试方便 全部放行
        return new AuthorizationDecision(true);
//        return new AuthorizationDecision(false);
    }
}
